home *** CD-ROM | disk | FTP | other *** search
/ Software Vault: The Gold Collection / Software Vault - The Gold Collection (American Databankers) (1993).ISO / cdr47 / pctuto.zip / DISK3.EXE / lha / CHAP15-3.DOC < prev    next >
Text File  |  1990-07-19  |  32KB  |  830 lines

  1.  
  2.  
  3.  
  4.              Chapter 15 - Subroutines                                      157
  5.              ________________________
  6.  
  7.              What if both strings are in memory but we don't know which
  8.              segments they are in? In that case, the calling subroutine needs
  9.              to pass both the segment and the offset for both the from_string
  10.              and the to_string. Let's do this in C.
  11.  
  12.                  move_string ( from_string, to_string ) ;
  13.  
  14.              In C, this will pass the addresses of the arrays, not the arrays
  15.              themselves. C, once again, pushes these variables in right to
  16.              left order. If the compiler is set up to move both the segment
  17.              and the offset, then the generated C code will be:
  18.  
  19.                  mov  ax, seg to_string
  20.                  push ax
  21.                  mov  ax, offset to_string
  22.                  push ax
  23.                  mov  ax, seg from_string
  24.                  push ax
  25.                  mov  ax, offset to_string
  26.                  push ax
  27.                  call move_string
  28.                  add  sp, 8                    ; 4 pushes = 8 bytes
  29.  
  30.              On the 8086, the low two bytes are ALWAYS the offset and the high
  31.              two bytes are ALWAYS the segment. Remember, SEG gets the segment
  32.              starting address of the named variable.
  33.  
  34.              We will do the subroutine as a near routine. After setting up BP,
  35.              we will have:
  36.  
  37.                            to_string segment        bp + 10
  38.                            to_string offset         bp + 8
  39.                            from_string segment      bp + 6
  40.                            from_string offset       bp + 4
  41.                            old IP                   bp + 2
  42.                  bp   ->   old bp                   bp + 0
  43.  
  44.              In the subroutine we will have to move the segment and the offset
  45.              for each pointer. Luckily for us, there are two 8086 instructions
  46.              for doing this:
  47.  
  48.                  LDS (load DS) loads the first two bytes into the named
  49.                  register and the next two bytes into DS.
  50.  
  51.                  LES (load ES) loads the first two bytes into the named
  52.                  register and the next two bytes into ES.
  53.  
  54.              If we write:
  55.  
  56.                       LES  di, [bp + 8]
  57.  
  58.              then the 8086 will load the first two bytes (bp+8 and bp+9) into
  59.              DI and the next two bytes (bp+10 and bp+11) into ES. This loads
  60.  
  61.              ______________________
  62.  
  63.              The PC Assembler Tutor - Copyright (C) 1989 Chuck Nelson
  64.  
  65.  
  66.  
  67.  
  68.              The PC Assembler Tutor                                        158
  69.              ______________________
  70.  
  71.              the offset and the segment in the same instruction. If we write:
  72.  
  73.                       LDS  si, [bp + 4]
  74.  
  75.              the 8086 will load the first two bytes (bp+4 and bp+5) into SI
  76.              and the next two bytes (bp+6 and bp+7) into ES, loading both the
  77.              offset and segment in one instruction.
  78.  
  79.              LDS and LES allow you to load the offset into any full arithmetic
  80.              register (AX, BX, CX, DX, SI, DI, BP or SP) but you can't use AX,
  81.              CX or DX as addressing registers, so it only makes sense to load
  82.              BX, SI, DI and BP for use as pointers. The two strings will now
  83.              be addressed by DS:SI and ES:DI. DS is SI's normal segment, so we
  84.              don't need to do anything, but we need a segment override for
  85.              ES:DI. Here is the code for a C subroutine:
  86.  
  87.              A C string ends with a 0 byte, that is with a byte having the
  88.              numeric value 0. It can be any length, but we need to test each
  89.              byte to find out if it is 0.
  90.  
  91.              Notice that we are using (and changing) both DS and ES this time,
  92.              so we have to PUSH and POP them, just like other registers.
  93.  
  94.              ; - - - - -
  95.              move_string   proc near
  96.  
  97.                  FROM_POINTER  EQU  [bp+4]
  98.                  TO_POINTER    EQU  [bp+8]
  99.  
  100.                  push bp
  101.                  mov  bp, sp
  102.                  PUSHREGS  ds, es, ax, si, di
  103.  
  104.                  lds  si, FROM_POINTER
  105.                  les  di, TO_POINTER
  106.  
  107.              move_loop
  108.                  mov  al, [si]            ; source to al
  109.                  mov  es:[di], al         ; al to destination
  110.                  inc  si                  ; pointers to next byte
  111.                  inc  di
  112.                  and  al, al              ; is al 0?
  113.                  jnz  move_loop
  114.  
  115.                  POPREGS  ds, es, ax, si, di
  116.                  pop  bp
  117.                  ret                      ; calling routine pops variables
  118.  
  119.              move_string  endp
  120.              ; - - - - - 
  121.  
  122.              Basically, the only difference between this and the Pascal move
  123.              is that (1) here we check for 0 and there we had an actual count,
  124.              and (2) in Pascal we used "ret (4)" and here the calling routine
  125.              does the adjustment.
  126.  
  127.  
  128.  
  129.  
  130.  
  131.  
  132.              Chapter 15 - Subroutines                                      159
  133.              ________________________
  134.  
  135.              DRAWING THE STACK
  136.  
  137.              Each time that we have used the stack we have drawn a picture of
  138.              where everything is on the stack. In case you think that this is
  139.              some trivial little learning technique, I'm telling you now that
  140.              at the assembler level, if you are passing variables on the stack
  141.              and you don't make a diagram on a piece of paper of where
  142.              everything is, you are guaranteed to consistently reference
  143.              things by the wrong address. ALWAYS make a paper diagram that
  144.              includes BP, ALWAYS use EQU statements, and you'll avoid a lot of
  145.              mistakes.
  146.  
  147.  
  148.  
  149.              It is now time to get more complex. Everything that follows is
  150.              more advanced, so it requires some programming experience.
  151.              Everything that follows is about C modules and recursion. If this
  152.              gets too complicated or obscure, just skip to the summary at the
  153.              end of the chapter.
  154.  
  155.  
  156.              I am going to give you a sample subroutine in C and then show you
  157.              where all the variables go. The following is a complete C file:
  158.  
  159.              /* a complete C file  - - - - - - - - - - - */
  160.  
  161.              int            A, B ;
  162.              static int     C, D ;
  163.              extern int     E, F ;
  164.  
  165.              sample_routine ( G, H )
  166.              int G, *H ;
  167.              {
  168.                  int            I, J ;
  169.                  static int     K, L ;    
  170.  
  171.                  A = I ;   /* transfer the words around */
  172.                  B = G ;
  173.                  J = E ;
  174.                  F = C ;
  175.                  D = K ;
  176.                  *H = I ;
  177.  
  178.                  return ;
  179.              }
  180.  
  181.              /* end of C file   - - - - - - - - - - - - - */
  182.  
  183.              The only thing this routine does is tranfer the words around.
  184.              This is so you can see where things are stored and how they are
  185.              accessed. If you don't know C, the only thing you really need to
  186.              know here is that '*H' means that 'H' is the ADDRESS of an
  187.              integer, not the integer itself, while '*H' is the actual integer
  188.              that is being addressed. 
  189.  
  190.              For those of you who DO know C, you need to know exactly what
  191.              extern, static and static mean. extern means that it is in an
  192.  
  193.  
  194.  
  195.  
  196.              The PC Assembler Tutor                                        160
  197.              ______________________
  198.  
  199.              external file. The first 'static' (which is outside of any
  200.              subroutine) means that the data is INTERNAL to the file; it won't
  201.              be shared with other files. Variables A and B, which don't have
  202.              the word 'static' are GLOBAL and will be shared with other files.
  203.              The other 'static' (inside the subroutine) means that its address
  204.              is fixed in memory while I and J, which are not 'static' have
  205.              their addresses generated every time you call the program. Say
  206.              what? Yes, that's correct, every time you call the program they
  207.              can be in a different place. That's what allows recursion, and
  208.              you'll see how that is implemented in a second.
  209.  
  210.              Here is the equivalent program in assembler:
  211.  
  212.              ; - - - - - START DATA BELOW THIS LINE
  213.                  PUBLIC A, B
  214.                  EXTRN  E:WORD, F:WORD
  215.  
  216.              A   dw   ?
  217.              B   dw   ?
  218.              C   dw   ?
  219.              D   dw   ?
  220.              K   dw   ?
  221.              L   dw   ?
  222.              ; - - - - - END DATA ABOVE THIS LINE
  223.  
  224.              ; - - - - - START SUBROUTINE BELOW THIS LINE
  225.              sample_routine proc near
  226.  
  227.                  ADDRESS_OF_H EQU [bp + 6]
  228.                  G EQU  [bp + 4]
  229.  
  230.                  I EQU  [bp - 2]
  231.                  J EQU  [bp - 4]
  232.  
  233.                  push bp
  234.                  mov  bp, sp         ; set up bp
  235.                  sub  sp, 4          ; save space for I and J
  236.                  PUSHREGS ax, si
  237.  
  238.                  mov  ax, I          ; A = I
  239.                  mov  A, ax
  240.  
  241.                  mov  ax, G          ; B = G
  242.                  mov  B, ax
  243.  
  244.                  mov  ax, E          ; J = E
  245.                  mov  J, ax
  246.  
  247.                  mov  ax, C          ; F = C
  248.                  mov  F, ax
  249.  
  250.                  mov  ax, K          ; D = K
  251.                  mov  D, ax
  252.  
  253.                  mov  ax, I          ; *H = I
  254.                  mov  si, ADDRESS_OF_H
  255.                  mov  [si], ax
  256.  
  257.  
  258.  
  259.  
  260.              Chapter 15 - Subroutines                                      161
  261.              ________________________
  262.  
  263.  
  264.                  POPREGS  ax, si
  265.                  mov  sp, bp         ; readjust sp
  266.                  pop  bp
  267.                  ret            ; a C routine so calling routine pops
  268.  
  269.              sample_routine endp
  270.              ; - - - - - END SUBROUTINE ABOVE THIS LINE
  271.  
  272.              This time the setup is a little longer. It is:
  273.  
  274.                  push bp
  275.                  mov  bp, sp
  276.                  sub  sp, 4
  277.                  PUSHREGS ax, si
  278.  
  279.              I and J are not in the data segment, so we need to make space for
  280.              them somewhere, and we do it on the stack. We subtract 4 from sp
  281.              to provide ourselves with 4 bytes, two for I and two for J. There
  282.              are two EQU statements that say exactly where I and J will be. We
  283.              also push AX and SI because we will use them. After this setup,
  284.              we have:
  285.  
  286.                            address of H        bp + 6
  287.                            G                   bp + 4
  288.                            old IP              bp + 2
  289.                  bp ->     old bp              bp + 0
  290.                            I                   bp - 2
  291.                            J                   bp - 4
  292.                            old ax              bp - 6
  293.                  sp ->     old si              bp - 8
  294.  
  295.              We have created space for some variables below bp. This is
  296.              temporary and will disappear when we leave the subroutine. We do
  297.              our dummy calculations,{1} and then do the end adjustment. There
  298.              are two ways to readjust sp before returning:
  299.  
  300.                  add  sp, 4
  301.  
  302.              just adds the amount that we subtracted, so it winds up in the
  303.              same place. But the place it winds up is ALWAYS the address of
  304.              the old BP, and bp is now pointing to that, so
  305.  
  306.                  mov  sp, bp
  307.  
  308.              does exactly the same thing.
  309.  
  310.  
  311.  
  312.              ARE YOU REALLY DEMENTED?
  313.  
  314.              Yes, for those of you who are truly masochistic, we have -
  315.              ta-dah! - the Towers of Hanoi in assembler language.
  316.  
  317.              ____________________
  318.  
  319.                 1. And that's 'dummy' in more ways than one.
  320.  
  321.  
  322.  
  323.  
  324.              The PC Assembler Tutor                                        162
  325.              ______________________
  326.  
  327.              The Towers of Hanoi is a game with three posts and a number of
  328.              disks which have incrementally smaller diameters. At the
  329.              beginning of the game, all the disks are on post one, ordered by
  330.              size with the smallest on top and the largest on the bottom. For
  331.              five disks it looks like this:
  332.  
  333.                               (1)              (2)              (3)
  334.  
  335.                                 X                X                X     
  336.                                 X                X                X
  337.                               XXXXX              X                X   
  338.                              XXXXXXX             X                X    
  339.                             XXXXXXXXX            X                X      
  340.                            XXXXXXXXXXX           X                X   
  341.                           XXXXXXXXXXXXX          X                X
  342.                       XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
  343.  
  344.  
  345.              The object is to wind up with all the disks on post three in the
  346.              same order. The game has only one rule: You may never put a
  347.              larger disk on top of a smaller disk.
  348.  
  349.              The general solution to this problem is that if you have posts A,
  350.              B and C with N disks on post A and you want to move them to post
  351.              C, first move (N-1) disks to post B, move the bottom disk from
  352.              post A to post C, then move (N-1) disks from post B to post C.
  353.              This works out to be the optimal recursive solution. Try it out
  354.              with N = 1, N = 2 and N = 3. N = 4 is already complicated.
  355.  
  356.              I won't discuss the game further or how to get the solution. If
  357.              you don't know about it, you can look it up in either Doug
  358.              Cooper's "Oh! Pascal" or Robert Kruse's "Data Structures and
  359.              Program Design".
  360.  
  361.              The fact that I need to resort to such an unususl example to
  362.              illustrate recursion underlines the fundamental rule of
  363.              recursion, which is:
  364.  
  365.                  YOU SELDOM NEED TO USE RECURSION, BUT WHEN YOU NEED IT YOU
  366.                  REALLY REALLY NEED IT.
  367.  
  368.              First, here is the solution in C:
  369.  
  370.              /* the C solution - - - - - - - - - - - - - - - */
  371.  
  372.              #define MAXIMUM 9
  373.  
  374.              main ()
  375.              {
  376.                  int  count ;
  377.  
  378.                  while (1)
  379.                  {
  380.                       printf ("Enter a number less than 10.\n" ) ;
  381.                       scanf ( "%d", &count ) ;
  382.                       if ( count > MAXIMUM )
  383.                            continue ;
  384.  
  385.  
  386.  
  387.  
  388.              Chapter 15 - Subroutines                                      163
  389.              ________________________
  390.  
  391.                       towers_of_hanoi ( count, 1, 3, 2 ) ;
  392.                  }
  393.              }
  394.              /* - - - - - - - - - - - - - - - - - - - - - - - - - */
  395.              towers_of_hanoi ( count, from, to, via )
  396.              int  count, from, to, via ;
  397.              {
  398.                  if (count <= 0)
  399.                       return ;
  400.  
  401.                  count-- ;
  402.                  towers_of_hanoi ( count, from, via, to ) ;
  403.                  printf ("Move a disk from %1d to %1d.\n" , from, to ) ;
  404.                  towers_of_hanoi ( count, via, to, from ) ;
  405.                  return ;
  406.              }
  407.  
  408.  
  409.              Notice that by letting a routine call itself, we have reduced it
  410.              to just a few lines. And this is a problem that looks very
  411.              complex. Here comes the assembler equivalent of the C code:
  412.  
  413.  
  414.              ; + + + + + + + + + + + + + + + START DATA BELOW THIS LINE 
  415.               
  416.                  MAX_COUNT  EQU   9
  417.               
  418.              enter_message        db  "Enter a number less than 10", 0 
  419.              make_a_move_message  db  "Move a disk from " 
  420.              from_byte            db  "X to " 
  421.              to_byte              db  "X.", 0 
  422.               
  423.              ; + + + + + + + + + + + + + + + END DATA ABOVE THIS LINE 
  424.  
  425.              ; + + + + + + + + + + + + + + + START CODE BELOW THIS LINE 
  426.              outer_loop: 
  427.                     lea   ax, enter_message        ; count message  
  428.                     call  print_string 
  429.                     call  get_unsigned_byte        ; count in al 
  430.                     sub   ah, ah                   ; zero ah for 'push ax'
  431.                     cmp   al, MAX_COUNT            ; too big? 
  432.                     ja    outer_loop 
  433.               
  434.                     ; move from post 1 to post 3 via post 2 
  435.                     mov   bx, 2                    ; post 2 = 'via' 
  436.                     push  bx 
  437.                     mov   bx, 3                    ; post 3 = 'to' 
  438.                     push  bx 
  439.                     mov   bx, 1                    ; post 1 = 'from' 
  440.                     push  bx 
  441.                     push  ax                       ; al = count, ah = 0 
  442.                     call  towers_of_hanoi 
  443.                     add   sp, 8                    ; adjust the stack
  444.                     jmp   outer_loop 
  445.              ; + + + + + + + + + + + + + + + END CODE ABOVE THIS LINE 
  446.               
  447.              ; + + + + + + + + + + + + START SUBROUTINES BELOW THIS LINE 
  448.  
  449.  
  450.  
  451.  
  452.              The PC Assembler Tutor                                        164
  453.              ______________________
  454.  
  455.              towers_of_hanoi proc near 
  456.               
  457.                     VIA   EQU  [bp + 10] 
  458.                     TO    EQU  [bp + 8] 
  459.                     FROM  EQU  [bp + 6] 
  460.                     COUNT EQU  [bp + 4] 
  461.               
  462.                     push  bp                  ; set up bp 
  463.                     mov   bp, sp 
  464.                     push  ax 
  465.                     cmp   BYTE PTR COUNT, 0   ; if no disks, we are done 
  466.                     jbe   exit 
  467.               
  468.                     dec   BYTE PTR COUNT      ; 1 less disk to move 
  469.  
  470.                     ; first half  
  471.                     push  TO 
  472.                     push  VIA 
  473.                     push  FROM 
  474.                     push  COUNT 
  475.                     call  towers_of_hanoi 
  476.                     add   sp, 8               ; adjust the stack 
  477.               
  478.                     ; print the message 
  479.                     mov   al, FROM            ; get 'from' number
  480.                     add   al, '0'             ; convert to ascii 
  481.                     mov   from_byte, al       ; put into message
  482.                     mov   al, TO              ; get 'to' number
  483.                     add   al, '0'             ; convert to ascii 
  484.                     mov   to_byte, al         ; put into message
  485.                     lea   ax, make_a_move_message 
  486.                     call  print_string 
  487.               
  488.                     ; second half 
  489.                     push  FROM 
  490.                     push  TO 
  491.                     push  VIA 
  492.                     push  COUNT 
  493.                     call  towers_of_hanoi 
  494.                     add   sp, 8               ; adjust the stack 
  495.               
  496.              exit: 
  497.                     pop   ax 
  498.                     pop   bp 
  499.                     ret 
  500.               
  501.              towers_of_hanoi endp 
  502.              ; + + + + + + + + + + + + END SUBROUTINES ABOVE THIS LINE 
  503.  
  504.  
  505.              The main routine checks that the number you enter is not too big
  506.              and then calls 'towers_of_hanoi'. After setting up BP, the stack
  507.              looks like this:
  508.  
  509.  
  510.                            VIA post       bp + 10
  511.                            TO post        bp + 8
  512.  
  513.  
  514.  
  515.  
  516.              Chapter 15 - Subroutines                                      165
  517.              ________________________
  518.  
  519.                            FROM post      bp + 6
  520.                            count          bp + 4
  521.                            old IP         bp + 2
  522.                  bp   ->   old bp         bp + 0
  523.  
  524.              We make some EQU statements to define where each variable is and
  525.              set up BP. We are using only one register, so we have a single
  526.              PUSH instead of using PUSHREGS. We then check to see if the count
  527.              is 0. If it is we are done. If not, we decrement the count by 1
  528.              which gives us 'count - 1' and divide the problem into three
  529.              parts. Part 1 calls 'towers_of_hanoi' and moves 'count - 1' disks
  530.              from 'from' to 'via'. Part 2 prints a message of where to move
  531.              the bottom disk. It converts the post numbers into ascii and
  532.              inserts them in the string where the 'X's are.{2}  Part 3 calls
  533.              towers_of_hanoi again, this time moving the 'count - 1' disks
  534.              from 'via' to 'to'. Assemble and link it with ASMHELP. When you
  535.              run it, start with just 1 disk, then 2 then 3 etc. If you use the
  536.              maximum number (9), it will print 511 lines. For N disks, you
  537.              need (2 ** N) - 1 moves, so this gets very big very fast.
  538.  
  539.              If you still feel no need to draw a picture of the stack or to
  540.              use EQU statements, why don't you try writing this subroutine
  541.              using the actual pointer values, i.e:
  542.  
  543.                  push [bp + 6]
  544.  
  545.              and so on. See how long it takes and see how easy it is to read
  546.              once it's done. Can you get it to work correctly?
  547.  
  548.              By the way, how large does the stack get? Well, if you raised the
  549.              limit to 30 disks and entered 30, It would take your computer
  550.              about a year, running 24 hours a day, to complete the solution
  551.              (about 1 billion moves). The maximum stack size would be
  552.              ((disks+1) * stack_use_per_disk). The extra 1 is for the calling
  553.              routine. That is 31 * 14 bytes (including pushing IP, BP, and
  554.              AX), or a mere 434 bytes.
  555.  
  556.  
  557.  
  558.  
  559.  
  560.  
  561.  
  562.  
  563.  
  564.  
  565.  
  566.  
  567.  
  568.  
  569.  
  570.  
  571.  
  572.              ____________________
  573.  
  574.                 2 It uses the fact that for a data declaration, a variable 
  575.              name has the address of the first piece of data on that line.
  576.  
  577.  
  578.  
  579.  
  580.              The PC Assembler Tutor                                        166
  581.              ______________________
  582.  
  583.                                       SUMMARY
  584.  
  585.  
  586.              To call a procedure you use CALL with the procedure name:
  587.  
  588.                  call subroutine1
  589.  
  590.              Procedures may be either FAR or NEAR. If there is a mismatch
  591.              between which type the assembler thinks it is and which type it
  592.              really is, there will be an error, either at the assembler level
  593.              for internal subroutines or at the linker level for external
  594.              subroutines. You may override the default type for procedures
  595.              with PTR:
  596.  
  597.                  call NEAR PTR subroutine5
  598.                  call FAR PTR subroutine6
  599.  
  600.              This is normally needed if the procedure comes after the call in
  601.              the file and is not the default type.
  602.  
  603.              To allow other files to use a procedure you declare it PUBLIC
  604.              within its own segment.
  605.  
  606.                  PUBLIC  subroutine1
  607.  
  608.              To use a PUBLIC procedure from another file you declare it EXTRN,
  609.              stating which type it is:
  610.  
  611.                  EXTRN  subroutine1:NEAR, subroutine2:FAR
  612.  
  613.              You should make this declaration in the code segment and you
  614.              should make it before the procedure is referenced.
  615.  
  616.              A procedure is defined by giving a name followed by the word
  617.              'proc' (procedure) followed by either FAR or NEAR
  618.  
  619.                  subroutine1 proc near
  620.                  subroutine2 proc far
  621.  
  622.              and a procedure is ended by giving the procedure name followed by
  623.              'endp' (end of procedure):
  624.  
  625.                  subroutine2 endp
  626.  
  627.              One procedure must be ended before another is begun.
  628.  
  629.  
  630.              Data is normally passed from one procedure to another on the
  631.              stack:
  632.  
  633.                  push variable1
  634.                  push variable2
  635.                  push variable3
  636.                  call subroutine1
  637.  
  638.              If this is done, then the called procedure references this data
  639.              by using BP, the base pointer. The standard setup code is:
  640.  
  641.  
  642.  
  643.  
  644.              Chapter 15 - Subroutines                                      167
  645.              ________________________
  646.  
  647.  
  648.                  push bp             ; save old bp
  649.                  mov  bp, sp         ; set bp to current top of stack
  650.  
  651.              What the stack looks like at this point depends on whether it is
  652.              a near or a far procedure. For a near procedure, we have:
  653.  
  654.                            variable1           bp + 8
  655.                            variable2           bp + 6
  656.                            variable3           bp + 4
  657.                            old IP              bp + 2
  658.                  bp   ->   old BP              bp + 0
  659.  
  660.              For a far procedure we have:
  661.  
  662.                            variable1           bp + 10
  663.                            variable2           bp + 8
  664.                            variable3           bp + 6
  665.                            old CS              bp + 4
  666.                            old IP              bp + 2
  667.                  bp   ->   old BP              bp + 0
  668.  
  669.              Although it is theoretically possible to access these variables
  670.              by their pointer definition:
  671.  
  672.                       mov  ax, [bp + 10]
  673.  
  674.              It is much less error prone and much clearer to use EQU
  675.              statements:
  676.  
  677.                       VAR1 EQU [bp + 10]
  678.  
  679.                       mov  ax, VAR1
  680.  
  681.              If you are writing a recursive procedure and you need temporary
  682.              variables, you can allot space on the stack for these variables:
  683.  
  684.                       sub  sp, 6     ; room for 6 bytes of temp. variables
  685.  
  686.              This should be done before any other pushes are done:
  687.  
  688.                       push bp
  689.                       mov  bp, sp
  690.                       sub sp, 6
  691.                       PUSHREGS ax, bx, cx, dx
  692.  
  693.  
  694.              These variables should also be named with EQU statements, and as
  695.              always, you should draw a picture of what is on the stack:
  696.  
  697.                            variable1           bp + 10
  698.                            variable2           bp + 8
  699.                            variable3           bp + 6
  700.                            old CS              bp + 4
  701.                            old IP              bp + 2
  702.                  bp   ->   old BP              bp + 0
  703.                            VAR4                bp - 2
  704.  
  705.  
  706.  
  707.  
  708.              The PC Assembler Tutor                                        168
  709.              ______________________
  710.  
  711.                            VAR5                bp - 4
  712.                            VAR6                bp - 6
  713.  
  714.  
  715.                       VAR4 EQU  [bp - 2]
  716.                       VAR5 EQU  [bp - 4]
  717.                       VAR6 EQU  [bp - 6]
  718.  
  719.              Data which is passed to the procedure is at a positive offset to
  720.              BP while data that is created in the procedure is at a negative
  721.              offset to BP.
  722.  
  723.              If you have created a data area for yourself on the stack, then
  724.              you must eliminate it before leaving the procedure. There are two
  725.              ways of doing this. One way is to add back what you have
  726.              subtracted:
  727.  
  728.                       POPREGS ax, bx, cx, dx
  729.                       add  sp, 6
  730.                       pop  bp
  731.                       ret
  732.  
  733.              The other way is to give SP the value in BP because this is the
  734.              place where SP will wind up anyway:
  735.  
  736.                       POPREGS ax, bx, cx, dx
  737.                       mov  sp, bp
  738.                       pop  bp
  739.                       ret
  740.  
  741.              Use whichever one is clearer to you.
  742.  
  743.  
  744.              When you return from a procedure that has had data passed to it,
  745.              the data must be taken off the stack. There are two ways of doing
  746.              this. The C standard is that it is the calling program's
  747.              responsibility to do this:
  748.  
  749.                  push variable1
  750.                  push variable2
  751.                  push variable3
  752.                  call subroutine1
  753.                  add  sp, 6          ; 3 pushes = 6 bytes
  754.  
  755.              The Pascal standard is that you take them off the stack on the
  756.              return. There is a special return instruction for that:
  757.  
  758.                  ret (6)             ; 3 pushes = 6 bytes
  759.  
  760.  
  761.              DATA
  762.  
  763.              Data can be made available to other files and data can be
  764.              accessed from other files. To make data available, declare it
  765.              PUBLIC:
  766.  
  767.                  PUBLIC variable1, variable2, variable3
  768.  
  769.  
  770.  
  771.  
  772.              Chapter 15 - Subroutines                                      169
  773.              ________________________
  774.  
  775.  
  776.              To access PUBLIC data from other files, use an EXTRN statement
  777.              which includes the data type:
  778.  
  779.                  EXTRN  variable7:BYTE, variable8:WORD, variable9:DWORD
  780.                  EXTRN  variable10:QWORD, variable11:TBYTE
  781.  
  782.              This EXTRN statement must be in a segment which has the same
  783.              ASSUME segment register as will be used when accessing the data.
  784.              Normally this is DS, but it can be something else. For instance,
  785.              if the above EXTRN statements were in MORESTUFF and you have:
  786.  
  787.                  ASSUME es:MORESTUFF
  788.  
  789.              then every time you access variable8:
  790.  
  791.                  mov  dx, variable8
  792.  
  793.              the assembler will code an ES segment override. 
  794.  
  795.  
  796.              PUSHREGS and POPREGS
  797.  
  798.              When writing a subroutine, you should always save any registers
  799.              that you use by pushing them.
  800.  
  801.                  push ax
  802.                  push bx
  803.                  push cx
  804.  
  805.              They are then popped before returning
  806.  
  807.                  pop  cx
  808.                  pop  bx
  809.                  pop  ax
  810.  
  811.              In order to save a lot of lines of code, there are two macros,
  812.              PUSHREGS and POPREGS. They are designed so you may use a word
  813.              processor to copy them. PUSHREGS pushes in left to right order
  814.              and POPREGS pops in right to left order:
  815.  
  816.                  PUSHREGS ax, bx, cx, dx
  817.                  POPREGS ax, bx, cx, dx
  818.  
  819.              is a matched pair.
  820.  
  821.              LES and LDS
  822.  
  823.              LDS (load DS) loads the first two bytes into the named register
  824.              and the next two bytes into DS. LES (load ES) loads the first two
  825.              bytes into the named register and the next two bytes into ES.
  826.  
  827.                       les  si, [bp+6]
  828.                       lds  di, [bp+10]
  829.  
  830.